package ex1;

public class ParallelGameOfLife implements GameOfLife {

	boolean[][] outputField;
	Cell[][] matrix;
	int height;
	int width;
	int heightRemainder;
	int widthRemainder;
	private int hSplit;
	private int vSplit;
	private boolean[][] initialField;
	private int generations;

	public boolean[][] invoke(boolean[][] initialField, int hSplit, int vSplit,
			int generations) {

		init(initialField, hSplit, vSplit, generations);

		calcWorkersBoundaries();

		initBoard();
		// initialize according to input

		// create and initialize the workers matrix
		Worker[][] threads = new Worker[vSplit][];

		int firstCellX, firstCellY;

		firstCellX = 0;

		for (int i = 0; i < threads.length; i++) {
			threads[i] = new Worker[hSplit];
			firstCellY = 0;
			int height = this.height, width;
			if (i < heightRemainder)
				height++;
			for (int j = 0; j < threads[i].length; j++) {
				width = this.width;
				if (j < widthRemainder)
					width++;
				threads[i][j] = new Worker(matrix, outputField, threads,
						firstCellX, firstCellY, i, j, height, width);
				threads[i][j].setBorders();
				firstCellY += width;
			}
			firstCellX += height;
		}

		// start your work........
		for (int i = 0; i < threads.length; i++) {
			for (int j = 0; j < threads[i].length; j++) {
				threads[i][j].start();
			}
		}
		// wait until all workers have finished their work
		for (int i = 0; i < threads.length; i++) {
			for (int j = 0; j < threads[i].length; j++) {
				try {
					threads[i][j].join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		return outputField;
	}

	private void initBoard() {
		for (int i = 0; i < initialField.length; i++) {
			matrix[i] = new Cell[initialField[i].length];
			outputField[i] = new boolean[initialField[i].length];
			for (int j = 0; j < initialField[i].length; j++) {
				boolean status = initialField[i][j];
				matrix[i][j] = new Cell(status, generations);
			}
		}
	}

	private void calcWorkersBoundaries() {
		this.height = initialField.length / vSplit;
		this.heightRemainder = initialField.length % vSplit;
		this.width = initialField[0].length / hSplit;
		this.widthRemainder = initialField[0].length % hSplit;

	}

	private void init(boolean[][] initialField, int hSplit, int vSplit,
			int generations) {
		this.generations = generations;
		this.initialField = initialField;
		this.hSplit = hSplit;
		this.vSplit = vSplit;
		outputField = new boolean[initialField.length][];
		matrix = new Cell[initialField.length][];

	}

}
